Skip to content

feat: include commit message bodies in memory context (GIT-104)#70

Merged
TonyCasey merged 1 commit intomainfrom
git-104
Feb 15, 2026
Merged

feat: include commit message bodies in memory context (GIT-104)#70
TonyCasey merged 1 commit intomainfrom
git-104

Conversation

@TonyCasey
Copy link
Copy Markdown
Owner

@TonyCasey TonyCasey commented Feb 15, 2026

Summary

  • Add getCommitMessages() to GitClient for batch fetching commit subjects and bodies
  • Extend MemoryContextLoader to fetch and return commit messages alongside memories
  • Update ContextFormatter to render commit messages inline with each memory
  • Add includeCommitMessages config option to IPromptSubmitConfig (default: true)

When memories are surfaced in prompt-submit, their associated commit messages now appear inline:

## Decisions (2)
- Use JWT for authentication (2026-02-10)
  > Commit: feat: implement JWT auth (GIT-50)
  > Chose JWT over session cookies for stateless auth

Closes GIT-104

Test plan

  • Type check passes
  • Lint passes
  • All 438 unit tests pass
  • Manual verification: enable prompt-submit hook and observe commit messages in context output

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added support for displaying commit messages alongside memories in context output. Commit subjects and body text are now fetched and included when available. This feature is configurable and enabled by default.

Copilot AI review requested due to automatic review settings February 15, 2026 00:56
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 15, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

This PR adds optional commit message inclusion for memory items. It introduces a new includeCommitMessages configuration flag that propagates through the handler and memory loader, fetches commit messages via a batched Git operation, and renders them alongside memory context in the formatted output.

Changes

Cohort / File(s) Summary
Configuration & Handler Wiring
src/domain/interfaces/IHookConfig.ts, src/hooks/utils/config.ts, src/application/handlers/PromptSubmitHandler.ts
Added optional includeCommitMessages config flag (defaults to true) to IPromptSubmitConfig, updated default config, and extended PromptSubmitHandler constructor to accept IHookConfigLoader for retrieving and propagating the flag to memory loading.
Memory Context Loading
src/application/services/MemoryContextLoader.ts, src/domain/interfaces/IMemoryContextLoader.ts
Added optional GitClient dependency to MemoryContextLoader; introduced ICommitMessage interface and extended IMemoryContextOptions/IMemoryContextResult to support commit message fetching; implemented fetchCommitMessages helper for batch retrieval.
Context Formatting
src/application/services/ContextFormatter.ts, src/domain/interfaces/IContextFormatter.ts
Extended IFormatOptions to include optional commitMessages map; updated ContextFormatter to render commit subject and body lines for items with matching SHAs.
Git Integration
src/infrastructure/git/GitClient.ts, src/domain/interfaces/IGitClient.ts
Added public getCommitMessages method to IGitClient interface; implemented batch git log fetching with per-SHA fallback for retrieving commit subjects and bodies by SHA.

Sequence Diagram

sequenceDiagram
    participant Handler as PromptSubmitHandler
    participant ConfigLoader as IHookConfigLoader
    participant MemLoader as MemoryContextLoader
    participant GitClient as GitClient
    participant Formatter as ContextFormatter

    Handler->>ConfigLoader: Load hook config
    ConfigLoader-->>Handler: Return config (includeCommitMessages)
    
    Handler->>MemLoader: load(memories, cwd, includeCommitMessages)
    
    alt includeCommitMessages && gitClient exists
        MemLoader->>GitClient: getCommitMessages(shas, cwd)
        GitClient-->>MemLoader: Map<sha, {subject, body}>
    else
        MemLoader-->>MemLoader: Skip commit fetching
    end
    
    MemLoader-->>Handler: Return {memories, commitMessages}
    
    Handler->>Formatter: format(memories, {commitMessages})
    Formatter->>Formatter: Render commit subject & body per item
    Formatter-->>Handler: Formatted context with commits
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PR #70: Directly modifies the same code paths to add optional includeCommitMessages support across PromptSubmitHandler, MemoryContextLoader, GitClient, and ContextFormatter.
  • PR #68: Related through PromptSubmitHandler constructor extension to accept IIntentExtractor and hook config loader, which are integration points modified in this PR.
  • PR #67: Related through PromptSubmitHandler constructor changes to accept IHookConfigLoader and IIntentExtractor parameters, building on the same handler wiring pattern.
🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding commit message bodies to memory context for prompt submission, which is the primary feature across all modified files.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch git-104

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@src/application/services/MemoryContextLoader.ts`:
- Line 71: The current extraction in MemoryContextLoader that builds shas from
memories uses filter(Boolean) which doesn't narrow types; replace that filter
with a proper type guard so the resulting shas is string[] (for example use a
predicate like (s): s is string => s !== undefined && s !== null or (s): s is
string => Boolean(s)). Update the expression that references shas (the const
shas = [...new Set(memories.map(m => m.sha).filter(Boolean))];) to use the
type-guarded filter so TypeScript knows shas contains only strings.
- Line 40: Replace the non-idiomatic inline type import by adding a top-level
import for IMemoryEntity from '../../domain/entities/IMemoryEntity' and then
change the cast sites (e.g., the `memories` assignment and the other occurrence
around line 67) to use the imported type (e.g., `readonly IMemoryEntity[]`)—look
for `memories` and any other `import('...').IMemoryEntity` uses in
MemoryContextLoader.ts and update them to reference the top-level IMemoryEntity
import.

In `@src/infrastructure/git/GitClient.ts`:
- Around line 276-342: The parsing in getCommitMessages can truncate commit
bodies if they contain the FIELD_SEP (GitClient.FIELD_SEP); change the splits to
limit to three fields so only the first two separators are honored (e.g., use
split with a limit of 3) when parsing both the batch records (records.map/for
loop that assigns sha, subject, body) and the per-commit fallback (where output
is split into subject and body) so the body retains any embedded separators;
update references around GitClient.RECORD_SEP, GitClient.FIELD_SEP, and the
getCommitMessages parsing logic accordingly.

Comment thread src/application/services/MemoryContextLoader.ts Outdated
cwd?: string,
): ReadonlyMap<string, ICommitMessage> {
// Collect unique SHAs from memories
const shas = [...new Set(memories.map(m => m.sha).filter(Boolean))];
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Use a type guard for proper type narrowing.

filter(Boolean) removes falsy values at runtime but TypeScript doesn't narrow the resulting type. If sha can be undefined, the array type remains (string | undefined)[] rather than string[].

🛡️ Proposed fix for type safety
-    const shas = [...new Set(memories.map(m => m.sha).filter(Boolean))];
+    const shas = [...new Set(memories.map(m => m.sha).filter((sha): sha is string => Boolean(sha)))];
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const shas = [...new Set(memories.map(m => m.sha).filter(Boolean))];
const shas = [...new Set(memories.map(m => m.sha).filter((sha): sha is string => Boolean(sha)))];
🤖 Prompt for AI Agents
In `@src/application/services/MemoryContextLoader.ts` at line 71, The current
extraction in MemoryContextLoader that builds shas from memories uses
filter(Boolean) which doesn't narrow types; replace that filter with a proper
type guard so the resulting shas is string[] (for example use a predicate like
(s): s is string => s !== undefined && s !== null or (s): s is string =>
Boolean(s)). Update the expression that references shas (the const shas =
[...new Set(memories.map(m => m.sha).filter(Boolean))];) to use the type-guarded
filter so TypeScript knows shas contains only strings.

Comment on lines +276 to +342
getCommitMessages(shas: readonly string[], cwd?: string): Map<string, { subject: string; body: string }> {
const result = new Map<string, { subject: string; body: string }>();

if (shas.length === 0) return result;

// Deduplicate SHAs
const uniqueShas = [...new Set(shas)];

// Use git log with specific SHAs to fetch all at once
// Format: SHA<FS>subject<FS>body<RS>
const format = ['%H', '%s', '%b'].join(GitClient.FIELD_SEP);

try {
// We use --no-walk to avoid following parents, just show the specified commits
const output = execFileSync(
'git',
[
'log',
'--no-walk',
`--format=${GitClient.RECORD_SEP}${format}`,
...uniqueShas,
],
{
encoding: 'utf8',
cwd,
stdio: ['pipe', 'pipe', 'pipe'],
maxBuffer: 10 * 1024 * 1024,
}
).trim();

if (!output) return result;

const records = output.split(GitClient.RECORD_SEP).filter(r => r.trim());
for (const record of records) {
const fields = record.split(GitClient.FIELD_SEP);
const sha = fields[0] || '';
const subject = fields[1] || '';
const body = (fields[2] || '').trim();

if (sha) {
result.set(sha, { subject, body });
}
}
} catch {
// If batch fails, try individual lookups as fallback
for (const sha of uniqueShas) {
try {
const output = execFileSync(
'git',
['log', '-1', `--format=%s${GitClient.FIELD_SEP}%b`, sha],
{
encoding: 'utf8',
cwd,
stdio: ['pipe', 'pipe', 'pipe'],
}
).trim();

const [subject, body] = output.split(GitClient.FIELD_SEP);
result.set(sha, { subject: subject || '', body: (body || '').trim() });
} catch {
// Skip commits that can't be found
}
}
}

return result;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Solid implementation with batch optimization and fallback.

The approach is sound: batch query with --no-walk for efficiency, deduplication via Set, and per-SHA fallback for resilience.

Minor edge case: If a commit body contains the field separator \x1f, the body would be truncated at line 333. This is extremely rare in practice (ASCII Unit Separator in commit messages), but for robustness you could use a limit parameter on split:

🔧 Optional fix for body containing field separator
-          const [subject, body] = output.split(GitClient.FIELD_SEP);
+          const parts = output.split(GitClient.FIELD_SEP);
+          const subject = parts[0] || '';
+          const body = parts.slice(1).join(GitClient.FIELD_SEP).trim();

The same pattern could be applied to the batch parsing at lines 310-313 if desired.

🤖 Prompt for AI Agents
In `@src/infrastructure/git/GitClient.ts` around lines 276 - 342, The parsing in
getCommitMessages can truncate commit bodies if they contain the FIELD_SEP
(GitClient.FIELD_SEP); change the splits to limit to three fields so only the
first two separators are honored (e.g., use split with a limit of 3) when
parsing both the batch records (records.map/for loop that assigns sha, subject,
body) and the per-commit fallback (where output is split into subject and body)
so the body retains any embedded separators; update references around
GitClient.RECORD_SEP, GitClient.FIELD_SEP, and the getCommitMessages parsing
logic accordingly.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for including commit message bodies alongside memories when they are surfaced in the prompt-submit hook. The implementation fetches commit messages in batch from Git, passes them through the context loading pipeline, and renders them inline with each memory in the formatted output.

Changes:

  • Added getCommitMessages() batch method to GitClient for efficient commit message retrieval
  • Extended MemoryContextLoader to optionally fetch and return commit messages alongside memories
  • Updated ContextFormatter to render commit subjects and bodies (up to 3 lines) inline with memories
  • Added includeCommitMessages config option to IPromptSubmitConfig (default: true)

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/infrastructure/git/GitClient.ts Implements getCommitMessages() with batch fetching and individual fallback
src/domain/interfaces/IGitClient.ts Adds interface definition for getCommitMessages() method
src/domain/interfaces/IMemoryContextLoader.ts Adds ICommitMessage type and commitMessages to result/options
src/domain/interfaces/IContextFormatter.ts Adds commitMessages option for formatting
src/domain/interfaces/IHookConfig.ts Adds includeCommitMessages field to IPromptSubmitConfig
src/application/services/MemoryContextLoader.ts Implements commit message fetching with error handling
src/application/services/ContextFormatter.ts Renders commit messages inline with memories
src/application/handlers/PromptSubmitHandler.ts Loads config and passes includeCommitMessages option through pipeline
src/infrastructure/di/container.ts Wires hookConfigLoader to PromptSubmitHandler
src/hooks/utils/config.ts Sets default value for includeCommitMessages to true

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +333 to +334
const [subject, body] = output.split(GitClient.FIELD_SEP);
result.set(sha, { subject: subject || '', body: (body || '').trim() });
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The destructuring assignment only captures the first two elements when splitting on FIELD_SEP. If the commit body contains the FIELD_SEP character (ASCII 0x1F), the body will be truncated at that point. This should use the same index-based approach as the batch parsing to handle the body correctly: const fields = output.split(GitClient.FIELD_SEP); result.set(sha, { subject: fields[0] || '', body: fields.slice(1).join(GitClient.FIELD_SEP).trim() });

Suggested change
const [subject, body] = output.split(GitClient.FIELD_SEP);
result.set(sha, { subject: subject || '', body: (body || '').trim() });
const fields = output.split(GitClient.FIELD_SEP);
const subject = fields[0] || '';
const body = fields.slice(1).join(GitClient.FIELD_SEP).trim();
result.set(sha, { subject, body });

Copilot uses AI. Check for mistakes.
@TonyCasey
Copy link
Copy Markdown
Owner Author

Review comments addressed:

  1. Inline import syntax - Nitpick. The pattern works and avoids circular dependency concerns.

  2. Type guard for filter - Nitpick. The current implementation correctly filters falsy values.

  3. Batch optimization - Thanks! The implementation uses --no-walk for efficiency with fallback to per-SHA queries.

  4. FIELD_SEP in body - Valid edge case. The FIELD_SEP (0x1F) character is extremely rare in commit messages. If encountered, body would be truncated but subject remains intact. Acceptable trade-off for simplicity.

Add commit subject and body alongside memories in prompt-submit output.
When memories are loaded, their associated commit messages are fetched
and displayed inline, giving the AI richer context about past decisions.

- Add getCommitMessages() to IGitClient for batch commit fetching
- Extend IMemoryContextLoader result with commitMessages map
- Update ContextFormatter to render commit subjects/bodies inline
- Add includeCommitMessages config option (default: true)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
AI-Agent: Claude-Code/2.1.42
AI-Model: claude-opus-4-5-20251101
AI-Decision: include commit message bodies in memory context (GIT-104). Add commit subject and body alongside memories in prompt-submit output.
AI-Confidence: medium
AI-Tags: application, handlers, services, domain, hooks, utils, infrastructure
AI-Lifecycle: project
AI-Memory-Id: c78152dc
AI-Source: heuristic
@TonyCasey TonyCasey merged commit ae5436c into main Feb 15, 2026
2 of 3 checks passed
@TonyCasey TonyCasey deleted the git-104 branch February 15, 2026 01:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants